#ifndef IODATASEGMENT_H_
#define IODATASEGMENT_H_

#include <string>
#include "status.hxx"
#include "util/io.hxx"
#include "db/datasegment.hxx"
#include "dbt.hxx"
#include <pthread.h>


class RandomAccessFile;
class IODataSegment;
class DataStoreImpl;
class DbPartition;

//datasegment file access for iodatasegment
class IODataSegmentFile {

private:
	DataSegmentHeader m_header;
	std::string m_segFileName;
	
	RandomAccessFile* m_raf;//for read and header write, also for data writing during recovery
	SequentialWriter* m_writer; //for appending	

	uint32_t m_lastSyncOffset; //sync to last position, not used yet

   
    //a readonly segment is changed only during recovery. The flag is used to
    //indicate some changes was made during recovery
    bool m_changed;

	static uint32_t DATASEGMENT_HEADER_LENGTH;

    DataSegment * m_seg;

    //a lock to proptect concurrent file operation: flush, append
    pthread_mutex_t m_mutex;

public:
	IODataSegmentFile(const std::string& segFileName, DataSegment* seg);
	~IODataSegmentFile();

	DbStatus init();

	DbStatus append(char* buf, uint32_t len);
	DbStatus append(const std::string& data);
	DbStatus read(uint32_t offset, uint32_t size, std::string& result);
    DbStatus read(uint32_t offset,
                  uint32_t size, 
                  char* buf,
                  uint32_t buflen, 
                  uint32_t& dlen);

	//for recovery
	DbStatus write(uint32_t offset, const std::string& data);
	DbStatus write(uint64_t offset, const char* buf, uint32_t n);

	DbStatus flush();

	uint32_t getNextOffset(){return m_writer->getNextOffset(); }

    void closeWriter();

    DataSegmentHeader& getHeader() {
        return m_header;
    }

    //drop os buffer cache for the segment file
    DbStatus dropCache();
    void setChanged() { m_changed = true; }

private:
	DbStatus saveHeader();
	DbStatus loadHeader();
};


//IODataSegment is good for the case when data cannot be fit into memory
class IODataSegment : public DataSegment {

private:
	IODataSegmentFile* m_segFile;
    DataStoreImpl* m_ds;
    DbPartition* m_partition;
    bool m_notifyCompact;

public:
    IODataSegment(uint32_t segId, const std::string& partitionPath, uint32_t capacity);
	IODataSegment(uint32_t segId, DataStoreImpl& ds, DbPartition& partition, uint32_t capacity);
	~IODataSegment();

	DbStatus init();


	uint32_t changeDataSize(int32_t size);
    uint32_t getActualDataSize();

    //the segment is reclaimed or not
    bool isAlive();
    void setAlive(bool live);
    
    //close appending fd
    void closeWriter();

    //appending operations
	DbStatus appendShort(uint16_t value);
	DbStatus appendInt(uint32_t value);
	DbStatus appendLong(uint64_t value);
	DbStatus append(const std::string& data, uint32_t& offset);
    DbStatus append(Dbt& dbt, uint32_t& offset);

    //read opertions
	DbStatus readShort(uint32_t offset, uint16_t& value);
	DbStatus readInt(uint32_t offset, uint32_t& value);
	DbStatus readLong(uint32_t offset, uint64_t& value);
    DbStatus read(uint32_t offset, uint32_t& size, std::string& result);
    DbStatus read(uint32_t offset, uint32_t& size, Dbt& dbt);

    //flush the appended data to disk
    DbStatus flush();

    //drop off os cache for the data segment if it has
    DbStatus dropCache();

	//for reocvery only
	DbStatus write(uint32_t offset, const std::string& data);
    void setDataSize(int32_t size);

    //compaction
    void notifyCompact();
};

#endif
